/*
 * Copyright 2011 Google Inc.
 * Copyright 2014 Andreas Schildbach
 * Copyright 2014-2016 the libsecp256k1 contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sq.util.encrypt.ecdsa;

import java.math.BigInteger;
import java.security.SecureRandom;

import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.math.ec.FixedPointCombMultiplier;

// TODO: Move this class to tracking compression state itself.
// The Bouncy Castle developers are deprecating their own tracking of the compression state.

/**
 * <p>
 * Represents an elliptic curve public and (optionally) private key, usable for
 * digital signatures but not encryption. Creating a new ECKey with the empty
 * constructor will generate a new random keypair. Other static methods can be
 * used when you already have the public or private parts. If you create a key
 * with only the public part, you can check signatures but not create them.
 * </p>
 *
 * <p>
 * ECKey also provides access to Bitcoin Core compatible text message signing,
 * as accessible via the UI or JSON-RPC. This is slightly different to signing
 * raw bytes - if you want to sign your own data and it won't be exposed as text
 * to people, you don't want to use this. If in doubt, ask on the mailing list.
 * </p>
 *
 * <p>
 * The ECDSA algorithm supports <i>key recovery</i> in which a signature plus a
 * couple of discriminator bits can be reversed to find the public key used to
 * calculate it. This can be convenient when you have a message and a signature
 * and want to find out who signed it, rather than requiring the user to provide
 * the expected identity.
 * </p>
 *
 * <p>
 * This class supports a variety of serialization forms. The methods that
 * accept/return byte arrays serialize private keys as raw byte arrays and
 * public keys using the SEC standard byte encoding for public keys. Signatures
 * are encoded using ASN.1/DER inside the Bitcoin protocol.
 * </p>
 *
 * <p>
 * A key can be <i>compressed</i> or <i>uncompressed</i>. This refers to whether
 * the public key is represented when encoded into bytes as an (x, y) coordinate
 * on the elliptic curve, or whether it's represented as just an X co-ordinate
 * and an extra byte that carries a sign bit. With the latter form the Y
 * coordinate can be calculated dynamically, however, <b>because the binary
 * serialization is different the address of a key changes if its compression
 * status is changed</b>. If you deviate from the defaults it's important to
 * understand this: money sent to a compressed version of the key will have a
 * different address to the same key in uncompressed form. Whether a public key
 * is compressed or not is recorded in the SEC binary serialisation format, and
 * preserved in a flag in this class so round-tripping preserves state. Unless
 * you're working with old software or doing unusual things, you can usually
 * ignore the compressed/uncompressed distinction.
 * </p>
 */
public class ECKey {

    private static final SecureRandom secureRandom;

    static {
        secureRandom = new SecureRandom();
    }

    // The two parts of the key. If "priv" is set, "pub" can always be
    // calculated. If "pub" is set but not "priv", we
    // can only verify signatures not make them.
    protected final BigInteger privateKey; // A field element.
    protected final LazyECPoint publicKey;

    /**
     * Generates an entirely new keypair. Point compression is used so the
     * resulting public key will be 33 bytes (32 for the co-ordinate and 1 byte
     * to represent the y bit).
     */
    public ECKey() {
        this(secureRandom, true);
    }

    public ECKey(SecureRandom secureRandom, boolean compressed) {
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        ECKeyGenerationParameters keygenParams = new ECKeyGenerationParameters(ECDSASignature.CURVE, secureRandom);
        generator.init(keygenParams);
        AsymmetricCipherKeyPair keypair = generator.generateKeyPair();
        ECPrivateKeyParameters privParams = (ECPrivateKeyParameters) keypair.getPrivate();
        ECPublicKeyParameters pubParams = (ECPublicKeyParameters) keypair.getPublic();
        privateKey = privParams.getD();
        publicKey = new LazyECPoint(ECDSASignature.CURVE.getCurve(), pubParams.getQ().getEncoded(compressed));
    }

    public ECKey(byte[] privKeyBytes, boolean compressed) {
        privateKey = new BigInteger(1, privKeyBytes);
        ECPoint point = publicPointFromPrivate(privateKey);
        publicKey = new LazyECPoint(ECDSASignature.CURVE.getCurve(), point.getEncoded(compressed));
    }

    public byte[] getPublicKey() {
        return publicKey.getEncoded();
    }

    public byte[] getPrivateKey() {
        return privateKey.toByteArray();
    }

    private static ECPoint publicPointFromPrivate(BigInteger privKey) {
        /*
         * TODO: FixedPointCombMultiplier currently doesn't support scalars
         * longer than the group order, but that could change in future
         * versions.
         */
        if (privKey.bitLength() > ECDSASignature.CURVE.getN().bitLength()) {
            privKey = privKey.mod(ECDSASignature.CURVE.getN());
        }
        return new FixedPointCombMultiplier().multiply(ECDSASignature.CURVE.getG(), privKey);
    }

}
